/*
OpenMVL Matrix Vector Library
Copyright (c) 2009 FluidInteractive (R)
 
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising
from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
 
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
 
Written by: Nicola Candussi <nicola@fluidinteractive.com>
*/

//graphics.hpp

#ifndef MVL_UTIL_GRAPHICS_HPP
#define MVL_UTIL_GRAPHICS_HPP

#include <cmath>
#include "../mvl.hpp"

//some utils useful when dealing with 3d graphics

#define e layout::e
#define _0_ layout::_0_
#define _1_ layout::_1_
#define _v_ layout::_v_

namespace mvl {

namespace util {

//4x4 rotation matrix around the x axis
template<typename T>
struct rotation_x_traits 
{
    typedef typename layout::make_sparse<
        4, 4, layout::row_major_tag,
        meta::vector<
            meta::vector<e<0, _1_> >,
            meta::ivector<1, 2>,
            meta::ivector<1, 2>,
            meta::vector<e<3, _1_> >
        >
    >::type layout_t;
    typedef mat<T, layout_t> type;
};

template<typename T>
inline 
typename rotation_x_traits<T>::type
rotation_x(T theta)
{
    typedef T value_type;
    typedef typename rotation_x_traits<T>::type result_t;
    value_type cos_theta(cos(theta));
    value_type sin_theta(sin(theta));
    return result_t(cos_theta, -sin_theta,
                    sin_theta, cos_theta);
}

//4x4 rotation matrix around the y axis
template<typename T>
struct rotation_y_traits 
{
    typedef typename layout::make_sparse<
        4, 4, layout::row_major_tag,
        meta::vector<
            meta::ivector<0, 2>,
            meta::vector<e<1, _1_> >,
            meta::ivector<0, 2>,
            meta::vector<e<3, _1_> >
        >
    >::type layout_t;
    typedef mat<T, layout_t> type;
};

template<typename T>
inline 
typename rotation_y_traits<T>::type
rotation_y(T theta)
{
    typedef T value_type;
    typedef typename rotation_y_traits<T>::type result_t;
    value_type cos_theta(cos(theta));
    value_type sin_theta(sin(theta));
    return result_t(cos_theta, sin_theta,
                    -sin_theta, cos_theta);
}

//4x4 rotation matrix around the z axis
template<typename T>
struct rotation_z_traits 
{
    typedef typename layout::make_sparse<
        4, 4, layout::row_major_tag,
        meta::vector<
            meta::ivector<0, 1>,
            meta::ivector<0, 1>,
            meta::vector<e<2, _1_> >,
            meta::vector<e<3, _1_> >
        >
    >::type layout_t;
    typedef mat<T, layout_t> type;
};

template<typename T>
inline 
typename rotation_z_traits<T>::type
rotation_z(T theta)
{
    typedef T value_type;
    typedef typename rotation_z_traits<T>::type result_t;
    value_type cos_theta(cos(theta));
    value_type sin_theta(sin(theta));
    return result_t(cos_theta, -sin_theta,
                    sin_theta, cos_theta);
}

//4x4 rotation around axis v
template<typename T, typename V>
struct rotation_traits 
{
    typedef typename layout::make_sparse<
        4, 4, layout::row_major_tag,
        meta::vector<
            meta::ivector<0, 1, 2>,
            meta::ivector<0, 1, 2>,
            meta::ivector<0, 1, 2>,
            meta::vector<e<3, _1_> >
        >
    >::type layout_t;
   
    //make sure V is size 3
    typedef typename meta::if_<
        typename meta::equal_to<typename V::Cols, meta::int_<1> >::type,
        range_mat_traits<0, 3, 0, 1, V>,
        range_mat_traits<0, 1, 0, 3, V>
    >::type VV;

    typedef typename trans_traits<VV>::type VV_trans;
    //take into account the fact that it could be a column vector or a row vector
    typedef typename meta::if_<
        typename meta::equal_to<typename V::Cols, meta::int_<1> >::type,
        VV,
        VV_trans
    >::type u_t;

    typedef typename meta::if_<
        typename meta::equal_to<typename V::Cols, meta::int_<1> >::type,
        VV_trans,
        VV
    >::type ut_t;
        
    //
    typedef typename u_t::const_closure_t u_closure_t; 
    typedef typename prod_traits<u_t, ut_t>::type uut_t;
    typedef typename uut_t::layout_t uut_layout_t;
    typedef mat<T, uut_layout_t> uut_closure_t; 
    //
    typedef mat<T, layout_t> type;
};

//WARNING: assume the axis is normalized
template<typename T, typename V>
inline 
typename rotation_traits<T, V>::type
rotation(T theta, V const& axis)
{
    typedef T value_type;
    typedef typename rotation_traits<T, V>::type result_t;

    value_type sin_theta = sin(theta);
    value_type cos_theta = cos(theta);

    typename rotation_traits<T, V>::u_closure_t u(axis);
    typedef typename rotation_traits<T, V>::uut_t uut_t;
    typename rotation_traits<T, V>::uut_closure_t uut(uut_t(u, u));

    result_t result;
    range_mat<0, 2, 0, 2> = uut +
                            cos_theta * (identity_mat<value_type, 3, 3>() - uut) +
                            sin_theta * skew_symmetric(u);
    return result;
}

//4x4 scale matrix

template<typename V>
struct scale_traits 
{
    typedef typename layout::make_sparse<
        4, 4, layout::row_major_tag,
        meta::vector<
            meta::ivector<0>,
            meta::ivector<1>,
            meta::ivector<2>,
            meta::vector<e<3, _1_> >
        >
    >::type layout_t;
    typedef typename V::value_type value_type;
    typedef layout::dense_layout<V::Rows::value, V::Cols::value, layout::row_major_tag> dense_layout_t;
    typedef mat<value_type, dense_layout_t> v_closure_t;
    typedef mat<value_type, layout_t> type;
};

template<typename V>
inline 
typename scale_traits<V>::type
scale(V const& s)
{
    typedef typename scale_traits<V>::value_type value_type;
    //convert to dense vector so we can access it's elements
    typename scale_traits<V>::v_closure_t sd(s);
    typedef typename scale_traits<V>::type result_t;
    return result_t(sd(0), sd(1), sd(2));
}


//4x4 translation matrix

template<typename V>
struct translate_traits 
{
    typedef typename layout::make_sparse<
        4, 4, layout::row_major_tag,
        meta::vector<
            meta::vector<e<0, _1_>, e<3, _v_> >,
            meta::vector<e<1, _1_>, e<3, _v_> >,
            meta::vector<e<2, _1_>, e<3, _v_> >,
            meta::vector<e<3, _1_> >
        >
    >::type layout_t;
    typedef typename V::value_type value_type;
    typedef layout::dense_layout<V::Rows::value, V::Cols::value, layout::row_major_tag> dense_layout_t;
    typedef mat<value_type, dense_layout_t> v_closure_t;
    typedef mat<value_type, layout_t> type;
};

template<typename V>
inline 
typename translate_traits<V>::type
translate(V const& t)
{
    typedef typename translate_traits<V>::value_type value_type;
    //convert to dense vector so we can access it's elements
    typename translate_traits<V>::v_closure_t td(t);
    typedef typename translate_traits<V>::type result_t;
    return result_t(td(0), td(1), td(2));
}

//4x4 right-handed perspective projection

template<typename T>
struct perspective_proj_rh_traits 
{
    typedef typename layout::make_sparse<
        4, 4, layout::row_major_tag,
        meta::vector<
            meta::ivector<0, 2>,
            meta::ivector<1, 2>,
            meta::ivector<2, 3>,
            meta::ivector<2>
        >
    >::type layout_t;
    typedef mat<T, layout_t> type;
};

template<typename T>
inline 
typename perspective_proj_rh_traits<T>::type
perspective_proj_rh(T l, T r, T b, T t, T n, T f)
{
    typedef typename perspective_proj_rh_traits<T>::type result_t;
    return result_t(2 * n / (r - l), (r + l) / (r - l),
                    2 * n / (t - b), (t + b) / (t - b),
                    -(f + n) / (f - n), -2 * f * n / (f - n),
                    -1);
}

//4x4 right-handed ortho projection

template<typename T>
struct ortho_proj_rh_traits 
{
    typedef typename layout::make_sparse<
        4, 4, layout::row_major_tag,
        meta::vector<
            meta::ivector<0, 3>,
            meta::ivector<1, 3>,
            meta::ivector<2, 3>,
            meta::vector<e<3, _1_> >
        >
    >::type layout_t;
    typedef mat<T, layout_t> type;
};

template<typename T>
inline 
typename ortho_proj_rh_traits<T>::type
ortho_proj_rh(T l, T r, T b, T t, T n, T f)
{
    typedef typename ortho_proj_rh_traits<T>::type result_t;
    return result_t(2 / (r - l), (r + l) / (r - l),
                    2 / (t - b), (t + b) / (t - b),
                    -2 / (f - n), (f + n) / (f - n));
}

} // namespace util

} // namespace mvl

#undef e
#undef _0_
#undef _1_
#undef _v_

#endif

